define(["dojo/_base/declare", "dojo/on", "dojo/query", "dojo/_base/array", "dojo/_base/lang", "dojo/_base/config"], function(declare, on, query, array, lang, config){

//dojo.require("dojo.AdapterRegistry");
// FIXME: refactor this to use an AdapterRegistry

  lang.getObject("dojox.storage", true);

  dojox.storage.manager = new function(){
    // summary:
    //		A singleton class in charge of the dojox.storage system
    // description:
    //		Initializes the storage systems and figures out the best available
    //		storage options on this platform.

    // currentProvider: Object
    //		The storage provider that was automagically chosen to do storage
    //		on this platform, such as dojox.storage.FlashStorageProvider.
    this.currentProvider = null;

    // available: Boolean
    //		Whether storage of some kind is available.
    this.available = false;

    // providers: Array
    //		Array of all the static provider instances, useful if you want to
    //		loop through and see what providers have been registered.
    this.providers = [];

    this._initialized = false;

    this._onLoadListeners = [];

    this.initialize = function(){
      // summary:
      //		Initializes the storage system and autodetects the best storage
      //		provider we can provide on this platform
      this.autodetect();
    };

    this.register = function(/*string*/ name, /*Object*/ instance){
      // summary:
      //		Registers the existence of a new storage provider; used by
      //		subclasses to inform the manager of their existence. The
      //		storage manager will select storage providers based on
      //		their ordering, so the order in which you call this method
      //		matters.
      // name:
      //		The full class name of this provider, such as
      //		"dojox.storage.FlashStorageProvider".
      // instance:
      //		An instance of this provider, which we will use to call
      //		isAvailable() on.

      // keep list of providers as a list so that we can know what order
      // storage providers are preferred; also, store the providers hashed
      // by name in case someone wants to get a provider that uses
      // a particular storage backend
      this.providers.push(instance);
      this.providers[name] = instance;
    };

    this.setProvider = function(storageClass){
      // summary:
      //		Instructs the storageManager to use the given storage class for
      //		all storage requests.
      // example:
      //	|	dojox.storage.setProvider(
      //	|		dojox.storage.IEStorageProvider)

    };

    this.autodetect = function(){
      // summary:
      //		Autodetects the best possible persistent storage provider
      //		available on this platform.

      //console.debug("dojox.storage.manager.autodetect");

      if(this._initialized){ // already finished
        return;
      }

      // a flag to force the storage manager to use a particular
      // storage provider type, such as
      // djConfig = {forceStorageProvider: "dojox.storage.WhatWGStorageProvider"};
      var forceProvider = config["forceStorageProvider"] || false;

      // go through each provider, seeing if it can be used
      var providerToUse;
      //FIXME: use dojo.some
      for(var i = 0; i < this.providers.length; i++){
        providerToUse = this.providers[i];
        if(forceProvider && forceProvider == providerToUse.declaredClass){
          // still call isAvailable for this provider, since this helps some
          // providers internally figure out if they are available
          // FIXME: This should be refactored since it is non-intuitive
          // that isAvailable() would initialize some state
          providerToUse.isAvailable();
          break;
        }else if(!forceProvider && providerToUse.isAvailable()){
          break;
        }
      }

      if(!providerToUse){ // no provider available
        this._initialized = true;
        this.available = false;
        this.currentProvider = null;
        console.warn("No storage provider found for this platform");
        this.loaded();
        return;
      }

      // create this provider and mix in it's properties
      // so that developers can do dojox.storage.put rather
      // than dojox.storage.currentProvider.put, for example
      this.currentProvider = providerToUse;
      lang.mixin(dojox.storage, this.currentProvider);

      // have the provider initialize itself
      dojox.storage.initialize();

      this._initialized = true;
      this.available = true;
    };

    this.isAvailable = function(){ /*Boolean*/
      // summary:
      //		Returns whether any storage options are available.
      return this.available;
    };

    this.addOnLoad = function(func){ /* void */
      // summary:
      //		Adds an onload listener to know when Dojo Offline can be used.
      // description:
      //		Adds a listener to know when Dojo Offline can be used. This
      //		ensures that the Dojo Offline framework is loaded and that the
      //		local dojox.storage system is ready to be used. This method is
      //		useful if you don't want to have a dependency on Dojo Events
      //		when using dojox.storage.
      // func: Function
      //		A function to call when Dojo Offline is ready to go
      this._onLoadListeners.push(func);

      if(this.isInitialized()){
        this._fireLoaded();
      }
    };

    this.removeOnLoad = function(func){ /* void */
      // summary:
      //		Removes the given onLoad listener
      for(var i = 0; i < this._onLoadListeners.length; i++){
        if(func == this._onLoadListeners[i]){
          this._onLoadListeners.splice(i, 1);
          break;
        }
      }
    };

    this.isInitialized = function(){ /*Boolean*/
      // summary:
      //		Returns whether the storage system is initialized and ready to
      //		be used.

      // FIXME: This should REALLY not be in here, but it fixes a tricky
      // Flash timing bug.
      // Confirm that this is still needed with the newly refactored Dojo
      // Flash. Used to be for Internet Explorer. -- Brad Neuberg
      if(this.currentProvider != null
          && this.currentProvider.declaredClass == "dojox.storage.FlashStorageProvider"
          && dojox.flash.ready == false){
        return false;
      }else{
        return this._initialized;
      }
    };

    this.supportsProvider = function(/*string*/ storageClass){ /* Boolean */
      // summary:
      //		Determines if this platform supports the given storage provider.
      // example:
      // |	dojox.storage.manager.supportsProvider(
      // |		"dojox.storage.InternetExplorerStorageProvider");

      // construct this class dynamically
      try{
        // dynamically call the given providers class level isAvailable()
        // method
        var provider = eval("new " + storageClass + "()");
        var results = provider.isAvailable();
        if(!results){ return false; }
        return results;
      }catch(e){
        return false;
      }
    };

    this.getProvider = function(){ /* Object */
      // summary:
      //		Gets the current provider
      return this.currentProvider;
    };

    this.loaded = function(){
      // summary:
      //		The storage provider should call this method when it is loaded
      //		and ready to be used. Clients who will use the provider will
      //		connect to this method to know when they can use the storage
      //		system. You can either use dojo.connect to connect to this
      //		function, or can use dojox.storage.manager.addOnLoad() to add
      //		a listener that does not depend on the dojo.event package.
      // example:
      // |	if(dojox.storage.manager.isInitialized() == false){
      // |		dojo.connect(dojox.storage.manager, "loaded", TestStorage, "initialize");
      // |	}else{
      // |		dojo.connect(dojo, "loaded", TestStorage, "initialize");
      // |	}
      // example:
      // |	dojox.storage.manager.addOnLoad(someFunction);


      // FIXME: we should just provide a Deferred for this. That way you
      // don't care when this happens or has happened. Deferreds are in Base
      this._fireLoaded();
    };

    this._fireLoaded = function(){
      //console.debug("dojox.storage.manager._fireLoaded");

      array.forEach(this._onLoadListeners, function(i){
        try{
          i();
        }catch(e){ console.debug(e); }
      });
    };

    this.getResourceList = function(){
      // summary:
      //		Returns a list of whatever resources are necessary for storage
      //		providers to work.
      // description:
      //		This will return all files needed by all storage providers for
      //		this particular environment type. For example, if we are in the
      //		browser environment, then this will return the hidden SWF files
      //		needed by the FlashStorageProvider, even if we don't need them
      //		for the particular browser we are working within. This is meant
      //		to faciliate Dojo Offline, which must retrieve all resources we
      //		need offline into the offline cache -- we retrieve everything
      //		needed, in case another browser that requires different storage
      //		mechanisms hits the local offline cache. For example, if we
      //		were to sync against Dojo Offline on Firefox 2, then we would
      //		not grab the FlashStorageProvider resources needed for Safari.
      var results = [];
      array.forEach(dojox.storage.manager.providers, function(currentProvider){
        results = results.concat(currentProvider.getResourceList());
      });

      return results;
    }
  };
});